/*
 * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "Creature.h"
#include "CreatureAISelector.h"
#include "PassiveAI.h"
#include "MovementGenerator.h"
#include "TemporarySummon.h"
#include "CreatureAIFactory.h"
#include "ScriptMgr.h"

INSTANTIATE_SINGLETON_1(CreatureAIRegistry);
INSTANTIATE_SINGLETON_1(MovementGeneratorRegistry);
INSTANTIATE_SINGLETON_1(GameObjectAIRegistry);

namespace FactorySelector
{
CreatureAI* selectAI(Creature* creature)
{
    const CreatureAICreator* ai_factory = NULL;
    CreatureAIRegistry& ai_registry(CreatureAIRepository::Instance());

    if (creature->IsPet())
        ai_factory = ai_registry.GetRegistryItem("PetAI");

    //scriptname in db
    if (!ai_factory)
        if (CreatureAI* scriptedAI = sScriptMgr.GetAI(creature))
            return scriptedAI;

    // AIname in db
    std::string ainame = creature->GetAIName();
    if (!ai_factory && !ainame.empty())
        ai_factory = ai_registry.GetRegistryItem(ainame.c_str());

    // select by NPC flags
    if (!ai_factory)
    {
        if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER)
            ai_factory = ai_registry.GetRegistryItem("PetAI");
        else if (creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK))
            ai_factory = ai_registry.GetRegistryItem("NullCreatureAI");
        else if (creature->IsGuard())
            ai_factory = ai_registry.GetRegistryItem("GuardAI");
        else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
            ai_factory = ai_registry.GetRegistryItem("PetAI");
        else if (creature->IsTotem())
            ai_factory = ai_registry.GetRegistryItem("TotemAI");
        else if (creature->IsTrigger())
        {
            if (creature->m_spells[0])
                ai_factory = ai_registry.GetRegistryItem("TriggerAI");
            else
                ai_factory = ai_registry.GetRegistryItem("NullCreatureAI");
        }
        else if (creature->GetCreatureType() == CREATURE_TYPE_CRITTER && !creature->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
            ai_factory = ai_registry.GetRegistryItem("CritterAI");
    }

    // select by permit check
    if (!ai_factory)
    {
        int best_val = -1;
        typedef CreatureAIRegistry::RegistryMapType RMT;
        RMT const& l = ai_registry.GetRegisteredItems();
        for (RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter)
        {
            const CreatureAICreator* factory = iter->second;
            const SelectableAI* p = dynamic_cast<const SelectableAI*>(factory);
            ASSERT(p != NULL);
            int val = p->Permit(creature);
            if (val > best_val)
            {
                best_val = val;
                ai_factory = p;
            }
        }
    }

    // select NullCreatureAI if not another cases
    ainame = (ai_factory == NULL) ? "NullCreatureAI" : ai_factory->key();

    DEBUG_LOG("Creature %u used AI is %s.", creature->GetGUIDLow(), ainame.c_str());
    return (ai_factory == NULL ? new NullCreatureAI(creature) : ai_factory->Create(creature));
}

MovementGenerator* selectMovementGenerator(Creature* creature)
{
    MovementGeneratorRegistry& mv_registry(MovementGeneratorRepository::Instance());
    ASSERT(creature->GetCreatureTemplate() != NULL);
    const MovementGeneratorCreator* mv_factory = mv_registry.GetRegistryItem(creature->GetDefaultMovementType());

    /* if (mv_factory == NULL)
    {
        int best_val = -1;
        std::vector<std::string> l;
        mv_registry.GetRegisteredItems(l);
        for (std::vector<std::string>::iterator iter = l.begin(); iter != l.end(); ++iter)
        {
        const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str());
        const SelectableMovement *p = dynamic_cast<const SelectableMovement *>(factory);
        ASSERT(p != NULL);
        int val = p->Permit(creature);
        if (val > best_val)
        {
            best_val = val;
            mv_factory = p;
        }
        }
    }*/

    return (mv_factory == NULL ? NULL : mv_factory->Create(creature));

}

GameObjectAI* SelectGameObjectAI(GameObject* go)
{
    const GameObjectAICreator* ai_factory = NULL;
    GameObjectAIRegistry& ai_registry(GameObjectAIRepository::Instance());

    // AIname in db
    std::string GobAiName = go->GetAIName();
    if (!GobAiName.empty())
        ai_factory = ai_registry.GetRegistryItem(GobAiName.c_str());

    //future goAI types go here

    std::string ainame = (ai_factory == NULL) ? "NullGameObjectAI" : ai_factory->key();

    DEBUG_LOG("GameObject %u used AI is %s.", go->GetGUIDLow(), ainame.c_str());

    return (ai_factory == NULL ? new NullGameObjectAI(go) : ai_factory->Create(go));
}
}

